package net.jcip.examples.chapter14;

/**
 * Synchronized关键字三种应用方式
 * 修饰实例成员方法：使用this锁，线程想要执行被Synchronized关键字修饰的成员实例方法必须先获取当前实例对象的锁资源；
 * 修饰静态成员方法：使用class锁，线程想要执行被Synchronized关键字修饰的静态方法必须先获取当前类对象的锁资源；
 * 修饰代码块：使用Object锁，使用给定的对象实现锁功能，线程想要执行被Synchronized关键字修饰的代码块必须先获取当前给定对象的锁资源；
 *
 *
 *
 *
 *上述代码中，我们开启t1,t2两个线程操作同一个共享资源即int变量i，由于自增操作i++;在我们上章节分析到该操作并不具备原子性，具体是分为三步来执行：
 * 1）先从主存中读取值；
 * 2）在自己工作内存进行+1操作；
 * 3）将结果刷新回主存。
 * 如果t2线程在t1线程读取旧值和写回新值期间（也就是t2在t1在自己工作内存中做+1计算时）读取全局资源i的值，那么t2线程就会与t1线程一起看到同一个值，并执行相同值的+1操作，这也就造成了线程安全失败，因此对于incr方法必须使用synchronized修饰，做到线程的互斥解决线程安全问题。
 * 此时我们应该注意到synchronized修饰的是对象实例方法incr()，在这样的情况下，当前线程的锁便是当前实例this锁，也就是当前实例对象syncIncrDemo，注意Java中的线程同步锁可以是任意对象（依赖于对象头中的Monitor，稍后会分析）。
 * 从代码执行结果来看确实是正确的，倘若我们没有使用synchronized关键字修饰incr()方法，其最终输出结果就有可能小于2000，这便是synchronized关键字的作用：
 *
 *
 */
public class SyncIncrDemo implements Runnable{


    //共享资源(临界资源)
    static int i = 0;

    //synchronized关键字修饰实例成员方法
    public synchronized void incr(){
        i++;
    }


    @Override
    public void run() {
        for(int j=0;j<1000;j++){
            incr();
        }
    }


    public static void main(String[] args) throws InterruptedException {
        SyncIncrDemo syncIncrDemo = new SyncIncrDemo();
        Thread t1=new Thread(syncIncrDemo);
        Thread t2=new Thread(syncIncrDemo);
        t1.start();
        t2.start();
        /**
         *join：使得放弃当前线程的执行，并返回对应的线程，例如下面代码的意思就是：
         程序在main线程中调用t1,t2线程的join方法，则main线程放弃cpu控制权，并返回
         t1,t2线程继续执行直到线程t1,t2执行完毕;
         所以结果是t1,t2线程执行完后，才到主线程执行，相当于在main线程中同步t1,t2
         线程，t1,t2执行完了，main线程才有执行的机会
         */
        t1.join();
        t2.join();
        System.out.println(i);
    }
    /**
     * 输出结果:
     * 2000
     */




}
